// map
// cb: (val, idx, arr)
export const map = (arrOrObj, cb) => {
  if (!arrOrObj) return arrOrObj;

  if (arrOrObj instanceof Array) return arrOrObj.map(cb);

  return [];
}

 // Join
export const join = (arrOrObj, seperator=',') => {
  if (!arrOrObj) return '';
  if (arrOrObj instanceof Array) return arrOrObj.join(seperator);
  if (arrOrObj instanceof Object) Object.keys(obj).map(key => obj[key]).join(seperator);

  return '';
}

export const len = (arrOrObj) => {
  if (!arrOrObj) return null;
  if (arrOrObj instanceof Array) {
    if (arrOrObj.length) return arrOrObj.length;
    return null;
  }
  if (arrOrObj instanceof Object) {
    if (Object.keys(arrOrObj).length) return Object.keys(arrOrObj).length;
    return null;
  };
  if (typeof arrOrObj == 'string') {
    return arrOrObj.length;
  }

  return null;
}

export const first = (s) => {
  if (!s) return null;
  if (s instanceof Array) {
    if (len(s)) return s[0];
  }
  if (s instanceof Object) {
    if (Object.keys(s).length) return s[Object.keys(s)[0]];
  };
  if (typeof s == 'string') {
    return s[0]; // FIXME:
  }

  return null;
}

export const last = (s) => {
  if (!s) return null;
  if (s instanceof Array) {
    if (len(s)) return s[len(s)-1];
  }


  return null;
}

// 反选(rejecting)
export const reject = (arrOrObj, keys) => {
  if (!arrOrObj) return null;

  if (arrOrObj instanceof Object) {
    return Object.keys(obj)
    .filter(k => !keys.includes(k))
    .map(k => ({[k]: obj[k]}))
    .reduce((res, o) => Object.assign(res, o), {});
  }
  
}

// 去重
export const unique = (arr, key) => {
  if (!key) {
    return arr.filter((ele, idx, self) => {
      return self.indexOf(ele) === idx;
    });
  }

  var obj = {};
  return arr.reduce((res, ele) => {
    if (ele) {
      (obj[ele[key]] ? false : (obj[ele[key]] = true)) && res.push(ele);
    }
    

    return res;
 }, []);
}

/**
 * 选取(picking)
 * 
 * 1. 支持数组、对象
 * 2. 支持降维字段筛选
 * 3. 支持返回三方字段/整个对象
 * 4. 支持返回默认值
 */
export const pick = (arrOrObj, dstVal, keyIn='', keyOut='', valOutDefault=null) => { // 应该用 drag，pick有很明确的语义
  if (!arrOrObj) return valOutDefault;

  if (arrOrObj instanceof Array) {
    if (keyIn && !keyOut) {
      // 返回整个对象
      keyOut = keyIn;
    }
  
    for (const idx in arrOrObj) {
      const obj = arrOrObj[idx];
  
      if (keyIn) {
        // 降单维查询
        if (obj[keyIn] == dstVal) {
          if (typeof keyOut == 'string') {
            return obj[keyOut];
          } else if (typeof keyOut == 'object') {
            return obj;
          }
        }
  
      } else {
        if (obj == dstVal) return idx;
      }
    }
  
    return '';
  }

  if (arrOrObj instanceof Object) {
    const keys = dstVal;
    return keys.map(k => k in arrOrObj ? {[k]: arrOrObj[k]} : {})
        .reduce((res, o) => Object.assign(res, o), {});
  }
  
  return null
}

export const pickV2 = (
    arrOrObj, 
    dstVal, 
    { 
      key, // key和后面两个*key互斥，该字段优先; 该key有效的时候，返回整个对象; 如果都无效，则返回idx
      keyIn, 
      keyOut, // inKey 有效的话，outKey可以为空，默认和outKey一样 
      valOutDefault 
    }
  ) => { // 应该用 drag，pick有很明确的语义
  if (!arrOrObj) return valOutDefault;

  // 值修复
  if (keyIn && !keyOut) { // inKey 为非空，outKey 为空，则返回整个对象
    // 返回整个对象
    keyOut = keyIn;
  }

  if (arrOrObj instanceof Array) {
    for (const idx in arrOrObj) {
      const obj = arrOrObj[idx];

      if (key) {
        if (obj[key] == dstVal) {
          return obj;
        }
      } else if (keyIn) {
        if (obj[keyIn] == dstVal) {
          return obj[keyOut]; 
        }
      } else {
        if (obj == dstVal) return idx;
      }
    }
  
    return '';
  }

  if (arrOrObj instanceof Object) {
    const keys = dstVal;
    return keys.map(k => k in arrOrObj ? {[k]: arrOrObj[k]} : {})
        .reduce((res, o) => Object.assign(res, o), {});
  }
  
  return null
}

export const isNumber = (val) => {
  if (typeof val == 'number') return true;

  var regPos = /^\d+(\.\d+)?$/; //非负浮点数
  var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数
  
  if(regPos.test(val) || regNeg.test(val)) {
    return true;
  } else {
    return false;
  }
}

export const bool2num = (val) => {
  return val ? 1 : 0;
}

export const objreduce = (obj) => { // FIXME: 命名有问题，功能是去掉对象中无效字段
  Object.keys(obj).forEach(k => {
    if (obj[k] !== 0 && !obj[k]) delete obj[k];
    if (obj[k] == 'undefined') delete obj[k];
  });

  

  return obj;
}

function getProp(obj, desc) {
  var arr = desc.split('.');
  while (arr.length) {
    obj = obj[arr.shift()];
  }
  return obj;
}

// @example
// var obj = {a: {b: {c: 0}}};
// var propPath = getPropPath(); // 例如返回 "a.b.c"
// var result = getDescendantProp(obj, propPath);

function setProp(obj, desc, value) {
  var arr = desc.split('.');
  while (arr.length > 1) {
    obj = obj[arr.shift()];
  }
  return obj[arr[0]] = value;
}

// @example
// var obj = {a: {b: {c: 0}}};
// var propPath = getPropPath();  // 例如，返回 "a.b.c"
// var result = setDescendantProp(obj, propPath, 1);  // a.b.c 值为 1

export const prop = function (obj, desc, value)  { // TODO: arguments 是 tuple ????
  const args = Array.prototype.slice.call(arguments);
  if (len(args) > 2) {
    setProp(obj, desc, value);
  } else {
    return getProp(obj, desc);
  }
}

export const asNumber = (val, fractionDigits=2) => { // null, undefined -> 0
  if (val === undefined || val === null) return parseFloat(0).toFixed(fractionDigits); // 0 == ''

  return parseFloat(val).toFixed(fractionDigits);
}

export const argsAnd = function () {
  const reducer = (accumulator, currentValue) => accumulator && currentValue;
  const args = Array.prototype.slice.call(arguments); // 不支持箭头函数

  return args.reduce(reducer)
}

export const argsOr = function () {
  const reducer = (accumulator, currentValue) => accumulator || currentValue;
  const args = Array.prototype.slice.call(arguments);

  return args.reduce(reducer)
}

export const formData2obj = formData => {
  let obj = {};
  for (var [a, b] of formData.entries()) {
    obj[a] = b;
    // console.log();
  } 
  return obj;
}

export const printFormData = function (formData) {
  
  console.log('formData = ', formData2obj(formData));
}

// 删除 数组和对象中 取反为真、字段取反为真的对象
export const nonnull = (s) => {
  if (!s) return null;
  if (s instanceof Array) {
    for (var i = 0 ; i < s.length; i++) {
      if (s[i] == " " || s[i] == null || typeof(s[i]) == "undefined") {
        s.splice(i, 1);
        i = i-1;
      }
    }
  }
  if (s instanceof Object) {
    for (const k in s) {
      !s[k] && (delete s[k])
    }
  };

  return s;
}